/*
 * TP1.cpp
 *
 *  Created on: 02/10/2012
 */

#include <iostream>
#include <stdlib.h>
#include <vector>
#include "Pipes/Pipe.h"
#include "Estacionamiento.h"
#include "Terminador.h"
#include "Constantes.h"
#include "Consulta.h"
#include "Entrada.h"
#include "Salida.h"
#include "Utils.h"
#include "Log.h"

using namespace std;

struct Parametros {
	int cantidadLugares;
	int costoHora;
	int tiempo;
	bool modoDebug;
};

void inicializarPipesSalidas(vector<Pipe>* pipesSalidas){
	Log::getInstance()->loguear("Se crean los pipes para comunicar los autos con las salidas.");

	for(int i = 0; i < CANTIDAD_SALIDAS; i++){
		pipesSalidas->push_back(Pipe());
	}
}

void inicializarSemaforosSalidas(vector<Semaforo>* semaforosSalidas){
	Log::getInstance()->loguear("Se crean semáforos para sincronizar los pipes de las salidas.");

	for(int i = 0; i < CANTIDAD_SALIDAS; i++){
		semaforosSalidas->push_back(Semaforo((char*)ARCHIVO_SALIDAS_SEMAFOROS, 1, (char) i));
	}
}

void eliminarSemaforosSalidas(vector<Semaforo>* semaforosSalidas){
	Log::getInstance()->loguear("Se eliminan los semáforos de las salidas.");

	for(vector<Semaforo>::iterator it = semaforosSalidas->begin(); it != semaforosSalidas->end(); it++){
		it->eliminar();
	}
}

int crearEntradas(Estacionamiento* estacionamiento, vector<Pipe>* pipesSalidas, vector<Semaforo>* semaforosSalidas, Terminador* terminador){
	for (int i = 0; i < CANTIDAD_ENTRADAS; i++){
		pid_t pid = fork();
		if (pid == 0){
			Entrada entrada(estacionamiento, i + 1, pipesSalidas, semaforosSalidas);
			entrada.recibirAutos();
			return 1;
		} else {
			terminador->agregarPidEntrada(pid);
		}
	}
	// Solo el proceso padre devuelve 0
	return 0;
}

int crearSalidas(Estacionamiento* estacionamiento, vector<Pipe>* pipesSalidas, Terminador* terminador){
	for (int i = 0; i < CANTIDAD_SALIDAS; i++){
		pid_t pid = fork();
		if (pid == 0){
			Salida salida(estacionamiento, i + 1, pipesSalidas);
			salida.inicializar();
			salida.sacarAutos();
			return 1;
		} else {
			terminador->agregarPidSalida(pid);
		}
	}
	// Solo el proceso padre devuelve 0
	return 0;
}

int crearConsulta(Estacionamiento* estacionamiento, vector<Pipe>* pipesSalidas, Terminador* terminador){
	pid_t pid = fork();
	if (pid == 0){
		Consulta consulta(estacionamiento, pipesSalidas);
		consulta.iniciarDeteccion();
		return 1;
	} else {
		terminador->setPidConsulta(pid);
	}
	// Solo el proceso padre devuelve 0
	return 0;
}

void imprimirUso(char* argv0, string mensaje){
	cerr << "Parámetros inválidos" << endl;
	cerr << "Uso: " << argv0 << " Lugares Costo Tiempo [-d | Modo]" << endl;
	cerr << mensaje << endl;
}

int validarParametros(int argc, char* argv[], Parametros* parametros){
	int retorno = 0;

	if (! ( argc == 4 || argc == 5)) {
		imprimirUso(argv[0], "Cantidad inválida de parámetros.");
		retorno = -1;
	} else {
		int i = 1;

		int c = atoi(argv[i++]);
		if (c > 0){
			parametros->cantidadLugares = c;
		} else {
			imprimirUso(argv[0], "Cantidad de lugares inválida. Debe ser mayor a 0.");
			retorno = -1;
		}

		c = atoi(argv[i++]);
		if (c > 0){
			parametros->costoHora = c;
		}
		else {
			imprimirUso(argv[0], "Costo de la hora inválida. Debe ser mayor a 0.");
			retorno = -1;
		}

		c = atoi(argv[i++]);
		if (c > 0){
			parametros->tiempo = c;
		}
		else {
			imprimirUso(argv[0], "Tiempo de simulación inválido. Debe ser mayor a 0.");
			retorno = -1;
		}

		parametros->modoDebug = false;
		if (argc == 5){
			char* modo = argv[i++];
			if (modo[0] == '-' && modo[1] == 'd'){
				parametros->modoDebug = true;
			}
		}
	}

	return retorno;
}

void crearArchivos(){
	Log::getInstance()->loguear("Se crean los archivos usados por las memorias compartidas y los semáforos.");

	crearArchivo(ARCHIVO_LUGARES_MEM_COMP);
	crearArchivo(ARCHIVO_LUGARES_SEMAFOROS);
	crearArchivo(ARCHIVO_CANTIDAD_AUTOS_MEM_COMP);
	crearArchivo(ARCHIVO_MONTO_FACTURADO_MEM_COMP);
	crearArchivo(ARCHIVO_TOTAL_AUTOS_MEM_COMP);
	crearArchivo(ARCHIVO_CUENTAS_SEMAFORO);
	crearArchivo(ARCHIVO_SALIDAS_SEMAFOROS);
}

int main(int argc, char *argv[]) {
	int retorno = 0;

	Parametros parametros;
	if ((retorno = validarParametros(argc, argv, &parametros)) == 0){

		Log::getInstance()->setModoDebug(parametros.modoDebug);
		Log::getInstance()->loguear(string(argv[0]) + " " + intToString(parametros.cantidadLugares) +
				" " + intToString(parametros.costoHora) + " " + intToString(parametros.tiempo));
		Log::getInstance()->imprimir("Comienza la simulación");

		crearArchivos();

		vector<Pipe> pipesSalidas;
		inicializarPipesSalidas(&pipesSalidas);

		vector<Semaforo> semaforosSalidas;
		inicializarSemaforosSalidas(&semaforosSalidas);

		// Se crea el terminador para finalizar la ejecución
		Terminador terminador(parametros.tiempo, &pipesSalidas);

		Estacionamiento estacionamiento(parametros.cantidadLugares, parametros.costoHora);
		estacionamiento.inicializar();

		if (crearEntradas(&estacionamiento, &pipesSalidas, &semaforosSalidas, &terminador) == 0){
			if (crearSalidas(&estacionamiento, &pipesSalidas, &terminador) == 0){
				if (crearConsulta(&estacionamiento, &pipesSalidas, &terminador) == 0){
					// Proceso padre
					terminador.inicializar();
					terminador.ejecutar();

					estacionamiento.imprimirEstadisticas();

					// Los semáforos se eliminan una sola vez en el proceso padre.
					estacionamiento.liberarRecursos();
					eliminarSemaforosSalidas(&semaforosSalidas);

				}
			}
		}

		Log::destruir();
	}

	return retorno;
}
